<?php
	
	/**
	 * Verifies bootstrapper constant definition, preventing direct
	 * execution of this page.
	 */
	defined('ENGINE') or exit;
	
	/**
	 * Primary Engine class; This acts as a mixed singleton/registry
	 * object.  This class is the sole core of the framework.
	 */
	final class Engine
	{
		/**
		 * ngine simple version, this contains the current 
		 * release in the form of: major.minor.release
		 */
		const VERSION					= '1.0.0';

		/**
		 * Engine version ID, this contains the version id in the form 
		 * of:
		 *
		 * id = (major_version * 10000) + (minor_version * 100) + release_version
		 */
		const VERSION_ID				= '10000';

		/**
		 * Engine version string, this is the full version string, which 
		 * includes the pre-release name, version and the version number 
		 * of the upcoming version if pre-release. For example:
		 */
		const VERSION_STRING			= '1.0.0 Alpha 1';
		
		/**
		 * For if we're in debug mode
		 */
		const DEBUG						= true;
		
		/**
		 * Holds the main instance
		 */
		private static $instance;
		
		/**
		 * Holds an array of other instances registered
		 */
		public $instances				= array();
		
		/**
		 * Holds the configuration array
		 */
		private $configuration			= array();
		
		/**
		 * Holds the list of global variables across the
		 * Engine
		 */
		private $globals				= array();
		
		/**
		 * Disable the ability to construct the registry
		 */
		private function __construct()
		{
		}
		
		/**
		 * Disable the ability to clone the registry
		 */
		private function __clone()
		{
		}
		
		/**
		 * Magic get method, this handles overloading of registered 
		 * instances
		 */
		public function __get($name)
		{
			if(array_key_exists($name, self::$instance->instances))
			{
				return(self::$instance->instances[$name]);
			}

			return(false);
		}
		
		/**
		 * Initializes a new object instance; This implements the
		 * singleton pattern and can be called from any context and
		 * will return the same object
		 */
		public static function init(Array $configuration = NULL)
		{
			if(!(self::$instance instanceof self))
			{
				self::$instance = new self;
			}

			if(is_array($configuration))
			{
				self::$instance->configuration = $configuration;
			}

			return(self::$instance);
		}
		
		/**
		 * Regsiters a new instance and makes it accessable through
		 * the name defined by the first parameter in the global scope
		 * similar to the reference below:
		 *
		 * <code>
		 * 		$engine = Engine::init();
		 * 		$engine->register('test', 'Classname');
		 *
		 *		$test->Methodname();
		 *		$engine->test->Methodname();
		 * </code>
		 */
		public function register($refname, $class)
		{
			if(array_key_exists($refname, self::$instance->instances))
			{
				return;
			}
			elseif(!class_exists($class))
			{
				throw new Engine_Basic_Exception('Passed object class (%s) does not exists', $class);
			}
			elseif(method_exists($class, 'invoke'))
			{
				$instance = call_user_func(array($class, 'invoke'), self::$instance, self::$instance->configuration);
			}

			self::$instance->set($refname, (isset($instance) ? $instance : new $class));
		}
		
		/**
		 * Sets a new reference in the registry
		 */
		public function set($refname, $reference)
		{
			$refname 			= strtolower($refname);
			$GLOBALS[$refname]	= self::$instance->instances[$refname] = $reference;
		}
		
		/**
		 * Gets a registered object instance
		 */
		public static function get($obj)
		{
			if(!array_key_exists($obj, self::$instance->instances))
			{
				return(false);
			}

			return(self::$instance->instances[$obj]);
		}
		
		/**
		 * Gets the configuration array
		 */
		public static function get_configuration()
		{
			if(isset(self::$instance->configuration))
			{
				return(self::$instance->configuration);
			}
			else if(isset($GLOBALS['configuration']))
			{
				return($GLOBALS['configuration']);
			}

			return(false);

		}
		
		/**
		 * Gets the options from the datastore
		 */
		public static function get_options()
		{
			static $options;
			
			if(is_array($options) || isset(self::$instance->instances['cache']) && ($options = self::$instance->instances['cache']->options))
			{
				return($options);
			}
			
			return(false);
		}
		
		/**
		 * Sets or gets a new global
		 */
		public static function globals($name, $value = NULL)
		{
			if(func_num_args() > 1)
			{
				self::$instance->globals[$name] = $value;
			}
			elseif(!array_key_exists($name, self::$instance->globals))
			{
				return(false);
			}

			return(self::$instance->globals[$name]);
		}
	}
	
	/**
	 * Default exception, mainly used for general errors. All 
	 * specific exceptions extend this exception.
	 */
	class Engine_Exception extends Exception
	{
		/**
		 * Indicates whenever this is a fatal error or not
		 */
		public function __construct()
		{
			$args = func_get_args();

			if(!sizeof($args))
			{
				$args[0] = 'Unknown error';
			}

			parent::__construct(call_user_func_array('sprintf', $args));
		}
	}
	
	/**
	 * Basic exception type, this is used for errors that 
	 * should act as fatal errors. If an exception of this 
	 * is caught by the default exception handler it will 
	 * terminate the execution.
	 */
	class Engine_Basic_Exception extends Engine_Exception
	{
	}
	
	/**
	 * Form data exception, this exception is used to display
	 * form errors, should they occur while processing
	 */
	class Engine_FormData_Exception extends Engine_Exception
	{
	}

?>